package storage.v1;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

import com.onpositive.data.IPropertyConstants;
import com.onpositive.mediawiki.dataminer.IReferencesStorage;
import com.onpositive.mediawiki.propertyparser.ParsedCategory;
import com.onpositive.mediawiki.propertyparser.ParsedDocument;
import com.onpositive.mediawiki.propertyparser.PropertyCharacteristic;
import com.onpositive.mediawiki.redirects.WikipediaRefsRedirector;
import com.onpositive.units.ParsedProperty;
import com.onpositive.wikistorage.CategoryLinksExtractor;
import com.onpositive.wikistorage.XMLInterface;

abstract public class LowLevelStorage extends CategoryStorage {

	/**
	 * 
	 */
	private static final long serialVersionUID = 4113935258921633398L;

	public static String RAW_DATA_FILE_NAME = "raw data.xml";

	protected ParserV2 parser;
	protected IReferencesStorage references;
	String rootPath;

	public LowLevelStorage(String path) {
		if(path!=null){
			this.rootPath = path.trim();
			this.rootPath += this.rootPath.endsWith("/") ? "" : "/";
			String fullPath = this.rootPath	+ RAW_DATA_FILE_NAME;
			category = XMLInterface.loadParsedCategory(fullPath);
		}		
		if (category == null)
			category = new ParsedCategory(this.rootPath);

		parser = new ParserV2();
		try {
			references = new DBStorage(WikipediaRefsRedirector.wikiDBImpl);
		} catch (Throwable e) {
			e.printStackTrace();
			//throw new RuntimeException(e);
			references=new IReferencesStorage() {
				
				@Override
				public void saveStorage() {}
				
				@Override
				public Collection<String> getParents(String name) {
					return null;
				}
				
				@Override
				public String getContent(String id, int flags) {
					return "";
				}
				
				@Override
				public Collection<String> getChildren(String name) {
					return null;
				}
			};
		}
	}

	protected ParsedDocument reparseDocument(String id, ReplacementRequest request,
			int flags) {
		// id = "list1";// table extraction test //
		ParsedDocument oldDoc = category!=null ? category.getParsedDocument(id) : null;
		String documentContent = references.getContent(id, flags);

		ParsedDocument newDoc = new ParsedDocument();
		newDoc.setName(id);
		newDoc.setCategory(category);
//XXX	newDoc.setParentCategoryNames(references.getParents(id));

		if (oldDoc != null) {
			newDoc.setName(oldDoc.getName());
			newDoc.setCategory(oldDoc.getCategory());
			ParsedProperty prop;
			prop = oldDoc
					.getProperty(IPropertyConstants.WWW_PICTURE_LINKS_PROPERTY_NAME);
//			if (prop != null)
//				newDoc.addProperty(prop);
			prop = oldDoc.getProperty(IPropertyConstants.WWW_PICTURE_COMMENTS);
			if (prop != null)
				newDoc.addProperty(prop);
			prop = oldDoc
					.getProperty(IPropertyConstants.PICTURE_LIST_PROPERTY_NAME);
			if (prop != null)
				newDoc.addProperty(prop);
		}
//XXX	new PictureInfoExtractor().process(documentContent, newDoc,null	,this);
		parser.parseDocument(documentContent, getParserProvider(), this, newDoc);
		// newDoc.extractLinks(category) ;
		newDoc.setDefaultPicture();

		if (oldDoc == null){
			if(category!=null){
				category.addParsedDocument(newDoc);
			}
		}
		else
			request.operate(oldDoc, newDoc);
		
		return newDoc;
	}

	public void reparse(String id) {
		LowLevelStorage.this.reparse(id, 0);
	}

	public void reparse(String id, int flags) {
		doReparse(id, flags);

		new ProperyDataExtractor().extractProperties(this, 0);
		processTableInfo();
		save();
		parser.dispose();
	}

	protected void doReparse(String id, int flags) {
		reparseDocument(id, new ReplacementRequest(), flags);
	}

	abstract protected List<String> getDocumentList();

	public void reparseAll() {

		// parallel for
		// Executor exec = Executors.newFixedThreadPool(4) ;
		for (String id : getDocumentList()) {
			// id="Mikoyan-Gurevich MiG-21" ;
			final String id_f = id;
			// exec.execute( new Runnable(){

			// @Override
			// public void run() {
			doReparse(id_f, 0);
			// }
			// }) ;

		}

		new ProperyDataExtractor().extractProperties(this, 0);
		processTableInfo();
		CategoryLinksExtractor.extractLinks(this.category);
		save();
		parser.dispose();
	}

	public void extractCategory(String inputCategory, ArrayList<String> ignoreList) {
		
		
		HashSet<String> toIgnore = new HashSet<String>();
		if(category!=null)
			toIgnore.addAll(category.getDocumentNamesList());
		if(ignoreList!=null)
			toIgnore.addAll(ignoreList);
		
		parseCategory(inputCategory, new ReplacementRequest(), toIgnore, 0);
		new ProperyDataExtractor().extractProperties(this, 0);
		processTableInfo();
		
		parseCategory(inputCategory, new ReplacementRequest(), toIgnore, FORCE_REPARSE_FLAG);
		new ProperyDataExtractor().extractProperties(this, 0);
		processTableInfo();

		CategoryLinksExtractor.extractLinks(this.category);
		save();
		parser.dispose();
	}

	// accepts FORCE_RELOAD_FLAG and FORCE_REPARSE_FLAG
	public void parseCategory(String inputCategory, ReplacementRequest request,
			HashSet<String> toIgnore, int flags) {

		HashSet<String> processedDocuments = new HashSet<String>();
		ArrayList<String> buffer = new ArrayList<String>();

		String redirectedCategoryName = inputCategory;
		// redirectedCategoryName = ParserV2.redirect(inputCategory) ;
		// if( redirectedCategoryName != null )
		// redirectedCategoryName = redirectedCategoryName.replace('_', ' ') ;
		//
		// else
		// redirectedCategoryName = inputCategory ;

		buffer.add(redirectedCategoryName);
		HashMap<String,ArrayList<String>> parentsMap = new HashMap<String, ArrayList<String>>(); 

		for (int i = 0; i < buffer.size(); i++) {
			String documentName = buffer.get(i);
			if(toIgnore!=null&&toIgnore.contains(documentName))
				continue;
			
			if (processedDocuments.contains(documentName))
				continue;

			if (category.getParsedDocument(documentName) != null
					&& (flags & FORCE_RELOAD_FLAG)
							+ (flags & FORCE_REPARSE_FLAG) == 0)
				continue;
			
			
			reparseDocument(documentName, request, flags);

			for (String str : references.getChildren(documentName)) {
				
				// TODO dirty hack here :-)
				if (badCategories.contains(str))
					continue;
				
				ArrayList<String> arr = parentsMap.get(str);
				if(arr == null){
					arr = new ArrayList<String>();
					parentsMap.put(str, arr);
				}
				arr.add(documentName);

				if (processedDocuments.contains(str))
					continue;

				

				// TODO redirection does not work on categories currently
				// String redirected = ParserV2.redirect(str) ;
				// if( redirected == null )
				// redirected = str ;
				//
				// if( badCategories.contains( redirected ) )
				// continue ;

				if (processedDocuments.contains(str))
					continue;
				
				buffer.add(str);				
			}

			processedDocuments.add(documentName);
		}
		updateParentCategories(parentsMap,toIgnore);
		references.saveStorage();
	}

	HashSet<String> badCategories = new HashSet<String>(
			Arrays.asList(new String[] { "Tank personnel",
					"Tank units and formations",
					"Military history of Italy during World War II" }));

	public void setProperty(WikiStorageProperty prop) {
		if (prop == null || prop.pChar == null || prop.pChar.getName() == null)
			return;

		PropertyCharacteristic pChar = prop.pChar;
		String propertyName = pChar.getName();

		// if( category == null )
		// category = new ParsedCategory( this.rootPath ) ;

		category.getChracteristicsMap().put(propertyName, pChar);
	}

	private void updateParentCategories(HashMap<String, ArrayList<String>> parentsMap, HashSet<String> toIgnore) {
		for (ParsedDocument doc : category.getDocumentsList()){			
			String name = doc.getName();
			if(toIgnore.contains(name))
				continue;
			
			ArrayList<String> arr = parentsMap.get(name);
			if(arr == null){
				arr = new ArrayList<String>();
			}
			ArrayList<String> lst = new ArrayList<String>(new HashSet<String>(arr));
			doc.setParentCategoryNames(lst);			
			//doc.setParentCategoryNames(references.getParents(doc.getName()));
		}
	}

	public Collection<String> getParents(String name) {
		return references.getParents(name);
	}

	public Collection<String> getChildren(String name) {
		return references.getChildren(name);
	}

	public String getRootPath() {
		final String crp = category.getRootPath();
		return crp + (crp.endsWith("/") || crp.endsWith("\\") ? "" : "/");
	}

	public boolean save() {
		XMLInterface.saveParsedCategory(
				this.getRootPath() + RAW_DATA_FILE_NAME, category, 0);
		return true;
	}
	
	public IParserProvider getParserProvider() {
		return DefaultParserProvider.getInstance();
	}

}
